import json
import time
import uuid
from datetime import datetime

from django.core.exceptions import ObjectDoesNotExist
from django_redis import get_redis_connection

from goods.models import Goods, SeckillGoods


class RedisHelper:
    def __init__(self, db_alias='default'):
        self.redis_client = get_redis_connection(db_alias)

    def set_cache(self, key, value, timeout=60):
        """
        设置缓存。
        根据值的类型选择存储方式：
        - 字典或列表：序列化为 JSON 字符串后存储。
        - 数字、字符串、布尔值：直接存储为字符串。
        """
        if isinstance(value, (dict, list)):
            # 如果是字典或列表，序列化为 JSON 字符串
            serialized_value = json.dumps(value)
        elif isinstance(value, (int, float, str, bool)):
            # 如果是数字、字符串或布尔值，直接转换为字符串
            serialized_value = str(value)
        else:
            raise ValueError("Unsupported data type. Value must be a dictionary, list, number, string, or boolean.")

        # 存储到 Redis，并设置过期时间
        self.redis_client.set(key, serialized_value, ex=timeout)

    def get_cache(self, key):
        """
        获取缓存。
        从 Redis 获取值并反序列化为字典或列表。
        如果数据不是有效的 JSON 格式，则返回 None。
        """
        # 从 Redis 获取值，可能是 bytes 类型
        value = self.redis_client.get(key)

        if value is None:  # 如果键不存在，直接返回 None
            return None

        # 将 bytes 解码为字符串
        try:
            value_str = value.decode('utf-8')
        except UnicodeDecodeError:
            # 如果解码失败，说明数据不是 UTF-8 编码
            return None  # 或者返回原始的 bytes 数据，视需求而定

        # 尝试将字符串反序列化为 JSON 对象
        try:
            return json.loads(value_str)  # 反序列化为字典或列表
        except json.JSONDecodeError:
            # 如果不是合法的 JSON 格式，返回 None
            return None

    def delete_cache(self, key):
        """删除缓存"""
        self.redis_client.delete(key)

    def acquire_lock(self, lock_key, expire_time=10):
        """获取分布式锁"""
        lock_value = str(uuid.uuid4())
        result = self.redis_client.set(lock_key, lock_value, nx=True, ex=expire_time)
        return lock_value if result else None

    def release_lock(self, lock_key, lock_value):
        """释放分布式锁"""
        script = """
        if redis.call("get", KEYS[1]) == ARGV[1] then
            return redis.call("del", KEYS[1])
        else
            return 0
        end
        """
        self.redis_client.eval(script, 1, lock_key, lock_value)

    def get_all_seckill_goods_ids(self):
        """获取所有秒杀商品的 ID"""
        pattern = "goods_detail:*"
        keys = self.redis_client.scan_iter(match=pattern)
        goods_ids = [key.decode("utf-8").split(":")[1] for key in keys]  # 手动解码字节类型
        return goods_ids

    def decrement_stock(self, stock_key):
        """
        原子性扣减库存。
        :param stock_key: 库存键
        :return: 扣减前的库存值（如果库存不足则返回 0）
        """
        lua_script = """
        if (redis.call("exists", KEYS[1]) == 1) then
            local stock = tonumber(redis.call("get", KEYS[1]))
            if (stock > 0) then
                redis.call("incrby", KEYS[1], -1)
                return stock
            end
            return 0
        end
        return -1
        """
        result = self.redis_client.eval(lua_script, 1, stock_key)
        return int(result) if result else 0


def preload_stock_to_redis(seckill_goods_id):
    """预加载秒杀商品库存到 Redis"""
    redis_client = get_redis_connection('default')
    stock_key = f'seckill_stock:{seckill_goods_id}'
    if not redis_client.exists(stock_key):
        try:
            seckill_goods = SeckillGoods.objects.get(id=seckill_goods_id)
            redis_client.set(stock_key, seckill_goods.stock_count, ex=3600)  # 设置过期时间为 1 小时
        except SeckillGoods.DoesNotExist:
            raise ValueError(f"秒杀商品 ID {seckill_goods_id} 不存在")


def get_goods_detail(goods_id):
    redis_client = RedisHelper()
    cache_key = f'goods_detail:{goods_id}'

    # 尝试从 Redis 缓存中获取商品信息
    goods_data = redis_client.get_cache(cache_key)
    if goods_data:
        return goods_data  # 直接返回反序列化的字典

    try:
        # 从数据库加载普通商品信息
        goods = Goods.objects.prefetch_related('seckill_goods').get(id=goods_id)
        goods_data = {
            'id': goods.id,
            'name': goods.goods_name,
            'price': str(goods.goods_price),  # Decimal 类型需要转换为字符串
            'stock_count': goods.goods_stock,
        }

        # 检查是否为秒杀商品
        try:
            seckill_goods = goods.seckill_goods.get(goods_id=goods_id)
            current_time = datetime.now()
            if seckill_goods.start_date <= current_time <= seckill_goods.end_date:
                # 如果当前时间在秒杀活动期间，添加秒杀商品信息
                goods_data.update({
                    'is_seckill': True,
                    'seckill_price': str(seckill_goods.seckill_price),
                    'seckill_stock': seckill_goods.stock_count,
                    'start_date': seckill_goods.start_date.isoformat(),
                    'end_date': seckill_goods.end_date.isoformat(),
                })
            else:
                goods_data['is_seckill'] = False
        except ObjectDoesNotExist:
            # 如果不是秒杀商品，标记为非秒杀
            goods_data['is_seckill'] = False

        # 将商品信息缓存到 Redis
        redis_client.set_cache(cache_key, goods_data, timeout=3600)

        return goods_data
    except Goods.DoesNotExist:
        raise ValueError(f"商品 ID {goods_id} 不存在")

def get_all_seckill_goods_ids():
    redis = RedisHelper()
    pattern = "goods_detail:*"
    keys = redis.redis_client.scan_iter(match=pattern)
    goods_ids = [key.split(":")[1] for key in keys]  # 提取 goods_id
    return goods_ids
